//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================


#include <ddk.h>
#include "dm9000.h"
#include "dm9000var.h"


//
// DM4If
//

class Dm4If : public CDeviceDriver {
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

public:

private:

};

extern Node g_nodereadlist;
extern Node g_nodewritelist;
extern PDM9000 pdm9000;
extern SystemTime tv1,tv2,tv3,tv4,tv5,tv6;
ECode Dm4If::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    PDM9000 pThis = (PDM9000)pdm9000;
    WaitResult wr;
    pThis->pRmutex->Lock(&wr);

    if(g_nodereadlist.IsEmpty()){
        pThis->pRcondition->Wait(pThis->pRmutex,&wr);
    }
//        DzGetSystemTime(&tv3);
//        kprintf("tv3:%08x_%08x\n", tv3.seconds, tv3.microseconds);
    Node * pnode = g_nodereadlist.GetNode();
    pThis->pRmutex->Unlock();
    if (pBuffer == NULL) {
        return E_INVALID_ARGUMENT;
    }
    pBuffer->Copy((Byte *)pnode->m_address, pnode->m_len);
//    kprintf("if=%d\n", ebbData.GetUsed());
//    for(int ii=0; ii<10; ii++){
//        kprintf("%08x ", *(pnode->m_address+ii));
//    }
//    kprintf("\n");
    free(pnode->m_address);
    delete pnode;
    return NOERROR;
}

ECode Dm4If::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    PDM9000 pThis = (PDM9000)pdm9000;

    unsigned long * p = (unsigned long *)malloc(buffer.GetUsed());
    if(p == NULL){
        kprintf("malloc failed\n");
        return S_FALSE;
    }
    Node* pnode = new Node(p, buffer.GetUsed());
    memcpy(p, buffer.GetPayload(), buffer.GetUsed());

//    kprintf("if=%d\n", buffer.GetUsed());
//    for(int ii=0; ii<80; ii++){
//        kprintf("%02x ", *(((char*)pnode->m_address)+14+28+0x16+ii));
//    }
//    kprintf("\n");

    WaitResult wr;
    pThis->pWmutex->Lock(&wr);
    g_nodewritelist.InsertNode(pnode);
    pThis->pWmutex->Unlock();
//    DzGetSystemTime(&tv4);
//        kprintf("tv3:%08x_%08x tv4:%08x_%08x\n", tv3.seconds, tv3.microseconds,
//                        tv4.seconds, tv4.microseconds);
    DM9000Xmit(pThis);
    return NOERROR;
}

ECode Dm4If::Control(
    /* [in] */  Handle32 nControlCode,
    /* [in] */ const  MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent ** ppCompletionEvent)
{
    if(nControlCode == DRVINITALIZE) {
        char * buf;
        if (pOutBuffer == NULL) {
            return E_INVALID_ARGUMENT;
        }
        dm9000_connect(NULL, (void**)&buf);
        pOutBuffer->Copy(buf, 6);
    }
    else if(nControlCode == DRVSHUTDOWN) {
        DM9000stop((PDM9000)pdm9000);
    }
    else {
        kprintf("unknown ControlCode\n");
    }
    return NOERROR;
}

void Dm4If::Dispose()
{
    delete this;
}

EXTERN IDeviceDriver * CDECL CreateDm9000(uint_t uDeviceNo, void *pvParameter)
{
    IDeviceDriver *pDriver;
    pDriver = new Dm4If();
    if (NULL == pDriver) return NULL;
    pDriver->AddRef();
    DzRegisterService(L"device:dm90000", (IInterface *)pDriver, NULL);
    dm9000new();
    return pDriver;
}
